package com.kymatrix.mpo.plus.bpm.server.convert.task;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.kymatrix.mpo.plus.bpm.server.flowable.util.FlowableUtils;
import com.kymatrix.mpo.plus.bpm.server.model.dto.message.BpmMessageSendWhenTaskCreatedReqDTO;
import com.kymatrix.mpo.plus.bpm.server.model.entity.definition.BpmFormEntity;
import com.kymatrix.mpo.plus.bpm.server.model.form.instance.QueryBpmProcessInstanceResp;
import com.kymatrix.mpo.plus.bpm.server.model.form.task.QueryBpmTaskResp;
import com.kymatrix.mpo.plus.common.business.common.PageResp;
import com.kymatrix.mpo.plus.common.util.BeanUtils;
import com.kymatrix.mpo.plus.common.util.DateUtil;
import com.kymatrix.mpo.plus.system.api.dto.DeptmentDto;
import com.kymatrix.mpo.plus.system.api.dto.UserDto;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.mapstruct.Mapper;

import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertList;
import static com.kymatrix.mpo.plus.common.util.CollectionUtils.convertMultiMap;
import static com.kymatrix.mpo.plus.common.util.CollectionUtils.filterList;
import static com.kymatrix.mpo.plus.common.util.MapUtils.findAndThen;


/**
 * Bpm 任务 Convert
 */
@Mapper
public class BpmTaskConvert {


    public static PageResp<QueryBpmTaskResp> buildTodoTaskPage(PageResp<Task> pageResult,
                                                           Map<String, ProcessInstance> processInstanceMap,
                                                           Map<String, UserDto> userMap) {
        PageResp<QueryBpmTaskResp> resp = new PageResp<>();
        resp.setTotal(pageResult.getTotal());
        for (Task task : pageResult.getList()) {
            QueryBpmTaskResp res = new QueryBpmTaskResp();
            res.setId(task.getId());
            res.setName(task.getName());
            res.setCreateTime(DateUtil.of(task.getCreateTime()));
            res.setDurationInMillis(task.getDueDate().getTime());
            res.setTaskDefinitionKey(task.getTaskDefinitionKey());
            res.setProcessInstanceId(task.getProcessInstanceId());
            
            
            if (processInstanceMap.containsKey(task.getProcessInstanceId())) {
                ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
                UserDto userDto = userMap.get(processInstance.getStartUserId());
                QueryBpmTaskResp.ProcessInstance pi = new QueryBpmTaskResp.ProcessInstance();
                pi.setId(processInstance.getProcessInstanceId());
                pi.setName(processInstance.getName());
                pi.setCreateTime(DateUtil.of(processInstance.getStartTime()));
                pi.setProcessDefinitionId(processInstance.getProcessDefinitionId());
                if (userDto != null) {
                    QueryBpmProcessInstanceResp.User user = new QueryBpmProcessInstanceResp.User();
                    user.setId(userDto.getId());
                    user.setNickname(userDto.getNickname());
                    user.setDeptId(userDto.getDeptId());
                    pi.setStartUser(user);
                }
                res.setProcessInstance(pi);
            }
            res.setParentTaskId(task.getParentTaskId());

            resp.getList().add(res);
        }
        return resp;
    }

    public static PageResp<QueryBpmTaskResp> buildTaskPage(PageResp<HistoricTaskInstance> pageResult,
                                                     Map<String, HistoricProcessInstance> processInstanceMap,
                                                     Map<String, UserDto> userMap,
                                                     Map<String, DeptmentDto> deptMap) {
        List<QueryBpmTaskResp> taskVOList = convertList(pageResult.getList(), task -> {
            QueryBpmTaskResp taskVO = BeanUtils.toBean(task, QueryBpmTaskResp.class);
            taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
            // 用户信息
            UserDto assignUser = userMap.get(task.getAssignee());
            if (assignUser != null) {
                taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, QueryBpmProcessInstanceResp.User.class));
                findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
            }
            // 流程实例
            HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());
            if (processInstance != null) {
                UserDto startUser = userMap.get(processInstance.getStartUserId());
                taskVO.setProcessInstance(BeanUtils.toBean(processInstance, QueryBpmTaskResp.ProcessInstance.class));
                taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, QueryBpmProcessInstanceResp.User.class));
            }
            return taskVO;
        });
        return new PageResp<>(taskVOList, pageResult.getTotal());
    }

    public static List<QueryBpmTaskResp> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
                                                                    HistoricProcessInstance processInstance,
                                                                    Map<String, BpmFormEntity> formMap,
                                                                    Map<String, UserDto> userMap,
                                                                    Map<String, DeptmentDto> deptMap) {
        List<QueryBpmTaskResp> taskVOList = convertList(taskList, task -> {
            QueryBpmTaskResp taskVO = BeanUtils.toBean(task, QueryBpmTaskResp.class);
            taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
            // 流程实例
            UserDto startUser = userMap.get(processInstance.getStartUserId());
            taskVO.setProcessInstance(BeanUtils.toBean(processInstance, QueryBpmTaskResp.ProcessInstance.class));
            taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, QueryBpmProcessInstanceResp.User.class));
            // 表单信息
            BpmFormEntity form = MapUtil.get(formMap, task.getFormKey(), BpmFormEntity.class);
            if (form != null) {
                taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())
                        .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
            }
            // 用户信息
            UserDto assignUser = userMap.get(task.getAssignee());
            if (assignUser != null) {
                taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, QueryBpmProcessInstanceResp.User.class));
                findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
            }
            UserDto ownerUser = userMap.get(task.getOwner());
            if (ownerUser != null) {
                taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, QueryBpmProcessInstanceResp.User.class));
                findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
            }
            return taskVO;
        });

        // 拼接父子关系
        Map<String, List<QueryBpmTaskResp>> childrenTaskMap = convertMultiMap(
                filterList(taskVOList, r -> StrUtil.isNotEmpty(r.getParentTaskId())),
                QueryBpmTaskResp::getParentTaskId);
        for (QueryBpmTaskResp taskVO : taskVOList) {
            taskVO.setChildren(childrenTaskMap.get(taskVO.getId()));
        }
        return filterList(taskVOList, r -> StrUtil.isEmpty(r.getParentTaskId()));
    }

    public static List<QueryBpmTaskResp> buildTaskListByParentTaskId(List<Task> taskList,
                                                               Map<String, UserDto> userMap,
                                                               Map<String, DeptmentDto> deptMap) {
        return convertList(taskList, task -> BeanUtils.toBean(task, QueryBpmTaskResp.class, taskVO -> {
            UserDto assignUser = userMap.get(task.getAssignee());
            if (assignUser != null) {
                taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, QueryBpmProcessInstanceResp.User.class));
                DeptmentDto dept = deptMap.get(assignUser.getDeptId());
                if (dept != null) {
                    taskVO.getAssigneeUser().setDeptName(dept.getName());
                }
            }
            UserDto ownerUser = userMap.get(task.getOwner());
            if (ownerUser != null) {
                taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, QueryBpmProcessInstanceResp.User.class));
                findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
            }
        }));
    }

    public static BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, UserDto startUser,
                                                        Task task) {
        BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
        reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
                .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
                .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
                .setAssigneeUserId(task.getAssignee());
        return reqDTO;
    }

    /**
     * 将父任务的属性，拷贝到子任务（加签任务）
     *
     * 为什么不使用 mapstruct 映射？因为 TaskEntityImpl 还有很多其他属性，这里我们只设置我们需要的。
     * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去，会出现意想不到的问题。
     *
     * @param parentTask 父任务
     * @param childTask 加签任务
     */
    public static void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) {
        childTask.setName(parentTask.getName());
        childTask.setDescription(parentTask.getDescription());
        childTask.setCategory(parentTask.getCategory());
        childTask.setParentTaskId(parentTask.getId());
        childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
        childTask.setProcessInstanceId(parentTask.getProcessInstanceId());
//        childTask.setExecutionId(parentTask.getExecutionId()); // TODO 芋艿：新加的，不太确定；尴尬，不加时，子任务不通过会失败（报错）；加了，子任务审批通过会失败（报错）
        childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
        childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId());
        childTask.setPriority(parentTask.getPriority());
        childTask.setCreateTime(new Date());
        childTask.setTenantId(parentTask.getTenantId());
    }

}
